package org.minuframework.mybatis.generator;

import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.tree.DefaultAttribute;
import org.dom4j.tree.DefaultElement;
import org.minuframework.dom4j.XMLReader;
import org.minuframework.dom4j.XMLWriter;
import org.minuframework.jdbc.JdbcConnection;
import org.minuframework.jdbc.JdbcProperties;
import org.minuframework.utils.ParamUtils;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.exception.InvalidConfigurationException;
import org.mybatis.generator.exception.XMLParserException;
import org.mybatis.generator.internal.DefaultShellCallback;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

public class MybatisGenerator{
    
    public static final String DEFAULT_GENERATOR_FILE       = "mybatis/mybatis-generator.xml";
    public static final String DEFAULT_TABLE_NAME           = "table";
    public static final String DEFAULT_JDBC_CONNECTION_NAME = "jdbcConnection";
    
    public File getDefaultGeneratorFile(){
        String pathname = System.getProperty("user.dir");
        return new File(pathname, DEFAULT_GENERATOR_FILE);
    }
    
    public boolean generator(){
        File file = new File(ParamUtils.getPathName(DEFAULT_GENERATOR_FILE));
        return generator(file, true);
    }
    
    public boolean generator(boolean table){
        return generator(getDefaultGeneratorFile(), table);
    }
    
    public boolean generator(File file, boolean table){
        if (table) {
            tables(file);
        }
        return generator(file);
    }
    
    public boolean generator(File file){
        if (file == null || !file.exists()) { return generator(); }
        boolean flag = true;
        try {
            ConfigurationParser parser = new ConfigurationParser(null);
            Configuration configuration = parser.parseConfiguration(file);
            DefaultShellCallback callback = new DefaultShellCallback(true);
            MyBatisGenerator generator = new MyBatisGenerator(configuration, callback, null);
            generator.generate(null);
        } catch (IOException | XMLParserException | InvalidConfigurationException | SQLException | InterruptedException e) {
            e.printStackTrace();
            flag = false;
        }
        return flag;
    }
    
    public boolean tables(){
        File file = new File(ParamUtils.getPathName(DEFAULT_GENERATOR_FILE));
        return tables(file);
    }
    
    public boolean tables(File file){
        if (file == null || !file.exists()) { return tables(); }
        Document document = XMLReader.read(file);
        Element root = document.getRootElement();
        Element context = root.element("context");
        tables(context);
        return XMLWriter.write(document, file);
    }
    
    public Element tables(Element element){
        JdbcConnection connection = new JdbcConnection();
        jdbcConnection(element, connection.getJdbcProperties());
        List<String> names = connection.getTableNames();
        tables(element, names);
        return element;
    }
    
    @SuppressWarnings("rawtypes")
    public Element tables(Element element, List<String> names){
        List tables = selectNodes(element, names);
        if (tables.isEmpty()) {
            for (String string : names) {
                Element table = element.addElement(DEFAULT_TABLE_NAME);
                table.addAttribute("tableName", string);
                table.addAttribute("enableCountByExample", "false");
                table.addAttribute("enableUpdateByExample", "false");
                table.addAttribute("enableDeleteByExample", "false");
                table.addAttribute("enableSelectByExample", "false");
                table.addAttribute("selectByExampleQueryId", "false");
            }
        } else {
            Map<String, Object> attributes = attributes((DefaultElement) tables.get(0));
            for (String string : names) {
                Element table = element.addElement(DEFAULT_TABLE_NAME);
                table.addAttribute("tableName", string);
                for (Entry<String, Object> entry : attributes.entrySet()) {
                    if (!entry.getKey().equals("tableName") && !entry.getKey().equals("domainObjectName")) {
                        table.addAttribute(entry.getKey(), entry.getValue().toString());
                    }
                }
            }
            
        }
        return element;
    }
    
    @SuppressWarnings({"rawtypes" ,"unchecked"})
    public List selectNodes(Element element, List<String> names){
        List result = Lists.newArrayList();
        List tables = element.selectNodes(DEFAULT_TABLE_NAME);
        Iterator iterator = tables.iterator();
        while (iterator.hasNext()) {
            DefaultElement defaultElement = (DefaultElement) iterator.next();
            String param = defaultElement.attribute("tableName").getData().toString();
            if (!names.contains(param)) {
                result.add(defaultElement);
            }
        }
        return result;
    }
    
    @SuppressWarnings("rawtypes")
    public Element removeNodes(Element element, List nodes){
        Iterator iterator = nodes.iterator();
        while (iterator.hasNext()) {
            DefaultElement defaultElement = (DefaultElement) iterator.next();
            element.remove(defaultElement);
        }
        return element;
    }
    
    public Element jdbcConnection(Element element, JdbcProperties jdbcProperties){
        if (element.element(DEFAULT_JDBC_CONNECTION_NAME) == null) {
            Element jdbc = element.addElement(DEFAULT_JDBC_CONNECTION_NAME);
            jdbc.addAttribute("driverClass", jdbcProperties.getDriver());
            jdbc.addAttribute("connectionURL", jdbcProperties.getUrl());
            jdbc.addAttribute("userId", jdbcProperties.getUsername());
            jdbc.addAttribute("password", jdbcProperties.getPassword());
        }
        return element;
    }
    
    @SuppressWarnings("rawtypes")
    public Map<String, Object> attributes(DefaultElement element){
        Map<String, Object> result = Maps.newHashMap();
        Iterator iterator = element.attributeIterator();
        while (iterator.hasNext()) {
            DefaultAttribute attribute = (DefaultAttribute) iterator.next();
            result.put(attribute.getName(), attribute.getValue());
        }
        return result;
    }
    
    public static void main(String[] args){
        MybatisGenerator generator = new MybatisGenerator();
        System.out.println(generator.generator(new File(ParamUtils.getPathName(DEFAULT_GENERATOR_FILE))));
    }
    
}
